/*
 * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.io;

import java.util.Formatter;
import java.util.Locale;
import java.nio.charset.Charset;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;

/**
 * A <code>PrintStream</code> adds functionality to another output stream,
 * namely the ability to print representations of various data values
 * conveniently.  Two other features are provided as well.  Unlike other output
 * streams, a <code>PrintStream</code> never throws an
 * <code>IOException</code>; instead, exceptional situations merely set an
 * internal flag that can be tested via the <code>checkError</code> method.
 * Optionally, a <code>PrintStream</code> can be created so as to flush
 * automatically; this means that the <code>flush</code> method is
 * automatically invoked after a byte array is written, one of the
 * <code>println</code> methods is invoked, or a newline character or byte
 * (<code>'\n'</code>) is written.
 *
 * <p> All characters printed by a <code>PrintStream</code> are converted into
 * bytes using the platform's default character encoding.  The <code>{@link
 * PrintWriter}</code> class should be used in situations that require writing
 * characters rather than bytes.
 *
 * @author Frank Yellin
 * @author Mark Reinhold
 * @since JDK1.0
 */

public class PrintStream extends FilterOutputStream
    implements Appendable, Closeable {

  private final boolean autoFlush;
  private boolean trouble = false;
  private Formatter formatter;

  /**
   * Track both the text- and character-output streams, so that their buffers
   * can be flushed without flushing the entire stream.
   */
  private BufferedWriter textOut;
  private OutputStreamWriter charOut;

  /**
   * requireNonNull is explicitly declared here so as not to create an extra
   * dependency on java.util.Objects.requireNonNull. PrintStream is loaded
   * early during system initialization.
   */
  private static <T> T requireNonNull(T obj, String message) {
    if (obj == null) {
      throw new NullPointerException(message);
    }
    return obj;
  }

  /**
   * Returns a charset object for the given charset name.
   *
   * @throws NullPointerException is csn is null
   * @throws UnsupportedEncodingException if the charset is not supported
   */
  private static Charset toCharset(String csn)
      throws UnsupportedEncodingException {
    requireNonNull(csn, "charsetName");
    try {
      return Charset.forName(csn);
    } catch (IllegalCharsetNameException | UnsupportedCharsetException unused) {
      // UnsupportedEncodingException should be thrown
      throw new UnsupportedEncodingException(csn);
    }
  }

  /* Private constructors */
  private PrintStream(boolean autoFlush, OutputStream out) {
    super(out);
    this.autoFlush = autoFlush;
    this.charOut = new OutputStreamWriter(this);
    this.textOut = new BufferedWriter(charOut);
  }

  private PrintStream(boolean autoFlush, OutputStream out, Charset charset) {
    super(out);
    this.autoFlush = autoFlush;
    this.charOut = new OutputStreamWriter(this, charset);
    this.textOut = new BufferedWriter(charOut);
  }

  /* Variant of the private constructor so that the given charset name
   * can be verified before evaluating the OutputStream argument. Used
   * by constructors creating a FileOutputStream that also take a
   * charset name.
   */
  private PrintStream(boolean autoFlush, Charset charset, OutputStream out)
      throws UnsupportedEncodingException {
    this(autoFlush, out, charset);
  }

  /**
   * Creates a new print stream.  This stream will not flush automatically.
   *
   * @param out The output stream to which values and objects will be printed
   * @see java.io.PrintWriter#PrintWriter(java.io.OutputStream)
   */
  public PrintStream(OutputStream out) {
    this(out, false);
  }

  /**
   * Creates a new print stream.
   *
   * @param out The output stream to which values and objects will be printed
   * @param autoFlush A boolean; if true, the output buffer will be flushed whenever a byte array is
   * written, one of the <code>println</code> methods is invoked, or a newline character or byte
   * (<code>'\n'</code>) is written
   * @see java.io.PrintWriter#PrintWriter(java.io.OutputStream, boolean)
   */
  public PrintStream(OutputStream out, boolean autoFlush) {
    this(autoFlush, requireNonNull(out, "Null output stream"));
  }

  /**
   * Creates a new print stream.
   *
   * @param out The output stream to which values and objects will be printed
   * @param autoFlush A boolean; if true, the output buffer will be flushed whenever a byte array is
   * written, one of the <code>println</code> methods is invoked, or a newline character or byte
   * (<code>'\n'</code>) is written
   * @param encoding The name of a supported <a href="../lang/package-summary.html#charenc">
   * character encoding</a>
   * @throws UnsupportedEncodingException If the named encoding is not supported
   * @since 1.4
   */
  public PrintStream(OutputStream out, boolean autoFlush, String encoding)
      throws UnsupportedEncodingException {
    this(autoFlush,
        requireNonNull(out, "Null output stream"),
        toCharset(encoding));
  }

  /**
   * Creates a new print stream, without automatic line flushing, with the
   * specified file name.  This convenience constructor creates
   * the necessary intermediate {@link java.io.OutputStreamWriter
   * OutputStreamWriter}, which will encode characters using the
   * {@linkplain java.nio.charset.Charset#defaultCharset() default charset}
   * for this instance of the Java virtual machine.
   *
   * @param fileName The name of the file to use as the destination of this print stream.  If the
   * file exists, then it will be truncated to zero size; otherwise, a new file will be created.
   * The output will be written to the file and is buffered.
   * @throws FileNotFoundException If the given file object does not denote an existing, writable
   * regular file and a new regular file of that name cannot be created, or if some other error
   * occurs while opening or creating the file
   * @throws SecurityException If a security manager is present and {@link
   * SecurityManager#checkWrite checkWrite(fileName)} denies write access to the file
   * @since 1.5
   */
  public PrintStream(String fileName) throws FileNotFoundException {
    this(false, new FileOutputStream(fileName));
  }

  /**
   * Creates a new print stream, without automatic line flushing, with the
   * specified file name and charset.  This convenience constructor creates
   * the necessary intermediate {@link java.io.OutputStreamWriter
   * OutputStreamWriter}, which will encode characters using the provided
   * charset.
   *
   * @param fileName The name of the file to use as the destination of this print stream.  If the
   * file exists, then it will be truncated to zero size; otherwise, a new file will be created.
   * The output will be written to the file and is buffered.
   * @param csn The name of a supported {@linkplain java.nio.charset.Charset charset}
   * @throws FileNotFoundException If the given file object does not denote an existing, writable
   * regular file and a new regular file of that name cannot be created, or if some other error
   * occurs while opening or creating the file
   * @throws SecurityException If a security manager is present and {@link
   * SecurityManager#checkWrite checkWrite(fileName)} denies write access to the file
   * @throws UnsupportedEncodingException If the named charset is not supported
   * @since 1.5
   */
  public PrintStream(String fileName, String csn)
      throws FileNotFoundException, UnsupportedEncodingException {
    // ensure charset is checked before the file is opened
    this(false, toCharset(csn), new FileOutputStream(fileName));
  }

  /**
   * Creates a new print stream, without automatic line flushing, with the
   * specified file.  This convenience constructor creates the necessary
   * intermediate {@link java.io.OutputStreamWriter OutputStreamWriter},
   * which will encode characters using the {@linkplain
   * java.nio.charset.Charset#defaultCharset() default charset} for this
   * instance of the Java virtual machine.
   *
   * @param file The file to use as the destination of this print stream.  If the file exists, then
   * it will be truncated to zero size; otherwise, a new file will be created.  The output will be
   * written to the file and is buffered.
   * @throws FileNotFoundException If the given file object does not denote an existing, writable
   * regular file and a new regular file of that name cannot be created, or if some other error
   * occurs while opening or creating the file
   * @throws SecurityException If a security manager is present and {@link
   * SecurityManager#checkWrite checkWrite(file.getPath())} denies write access to the file
   * @since 1.5
   */
  public PrintStream(File file) throws FileNotFoundException {
    this(false, new FileOutputStream(file));
  }

  /**
   * Creates a new print stream, without automatic line flushing, with the
   * specified file and charset.  This convenience constructor creates
   * the necessary intermediate {@link java.io.OutputStreamWriter
   * OutputStreamWriter}, which will encode characters using the provided
   * charset.
   *
   * @param file The file to use as the destination of this print stream.  If the file exists, then
   * it will be truncated to zero size; otherwise, a new file will be created.  The output will be
   * written to the file and is buffered.
   * @param csn The name of a supported {@linkplain java.nio.charset.Charset charset}
   * @throws FileNotFoundException If the given file object does not denote an existing, writable
   * regular file and a new regular file of that name cannot be created, or if some other error
   * occurs while opening or creating the file
   * @throws SecurityException If a security manager is present and {@link
   * SecurityManager#checkWrite checkWrite(file.getPath())} denies write access to the file
   * @throws UnsupportedEncodingException If the named charset is not supported
   * @since 1.5
   */
  public PrintStream(File file, String csn)
      throws FileNotFoundException, UnsupportedEncodingException {
    // ensure charset is checked before the file is opened
    this(false, toCharset(csn), new FileOutputStream(file));
  }

  /**
   * Check to make sure that the stream has not been closed
   */
  private void ensureOpen() throws IOException {
    if (out == null) {
      throw new IOException("Stream closed");
    }
  }

  /**
   * Flushes the stream.  This is done by writing any buffered output bytes to
   * the underlying output stream and then flushing that stream.
   *
   * @see java.io.OutputStream#flush()
   */
  public void flush() {
    synchronized (this) {
      try {
        ensureOpen();
        out.flush();
      } catch (IOException x) {
        trouble = true;
      }
    }
  }

  private boolean closing = false; /* To avoid recursive closing */

  /**
   * Closes the stream.  This is done by flushing the stream and then closing
   * the underlying output stream.
   *
   * @see java.io.OutputStream#close()
   */
  public void close() {
    synchronized (this) {
      if (!closing) {
        closing = true;
        try {
          textOut.close();
          out.close();
        } catch (IOException x) {
          trouble = true;
        }
        textOut = null;
        charOut = null;
        out = null;
      }
    }
  }

  /**
   * Flushes the stream and checks its error state. The internal error state
   * is set to <code>true</code> when the underlying output stream throws an
   * <code>IOException</code> other than <code>InterruptedIOException</code>,
   * and when the <code>setError</code> method is invoked.  If an operation
   * on the underlying output stream throws an
   * <code>InterruptedIOException</code>, then the <code>PrintStream</code>
   * converts the exception back into an interrupt by doing:
   * <pre>
   *     Thread.currentThread().interrupt();
   * </pre>
   * or the equivalent.
   *
   * @return <code>true</code> if and only if this stream has encountered an
   * <code>IOException</code> other than <code>InterruptedIOException</code>, or the
   * <code>setError</code> method has been invoked
   */
  public boolean checkError() {
    if (out != null) {
      flush();
    }
    if (out instanceof java.io.PrintStream) {
      PrintStream ps = (PrintStream) out;
      return ps.checkError();
    }
    return trouble;
  }

  /**
   * Sets the error state of the stream to <code>true</code>.
   *
   * <p> This method will cause subsequent invocations of {@link
   * #checkError()} to return <tt>true</tt> until {@link
   * #clearError()} is invoked.
   *
   * @since JDK1.1
   */
  protected void setError() {
    trouble = true;
  }

  /**
   * Clears the internal error state of this stream.
   *
   * <p> This method will cause subsequent invocations of {@link
   * #checkError()} to return <tt>false</tt> until another write
   * operation fails and invokes {@link #setError()}.
   *
   * @since 1.6
   */
  protected void clearError() {
    trouble = false;
  }

    /*
     * Exception-catching, synchronized output operations,
     * which also implement the write() methods of OutputStream
     */

  /**
   * Writes the specified byte to this stream.  If the byte is a newline and
   * automatic flushing is enabled then the <code>flush</code> method will be
   * invoked.
   *
   * <p> Note that the byte is written as given; to write a character that
   * will be translated according to the platform's default character
   * encoding, use the <code>print(char)</code> or <code>println(char)</code>
   * methods.
   *
   * @param b The byte to be written
   * @see #print(char)
   * @see #println(char)
   */
  public void write(int b) {
    try {
      synchronized (this) {
        ensureOpen();
        out.write(b);
        if ((b == '\n') && autoFlush) {
          out.flush();
        }
      }
    } catch (InterruptedIOException x) {
      Thread.currentThread().interrupt();
    } catch (IOException x) {
      trouble = true;
    }
  }

  /**
   * Writes <code>len</code> bytes from the specified byte array starting at
   * offset <code>off</code> to this stream.  If automatic flushing is
   * enabled then the <code>flush</code> method will be invoked.
   *
   * <p> Note that the bytes will be written as given; to write characters
   * that will be translated according to the platform's default character
   * encoding, use the <code>print(char)</code> or <code>println(char)</code>
   * methods.
   *
   * @param buf A byte array
   * @param off Offset from which to start taking bytes
   * @param len Number of bytes to write
   */
  public void write(byte buf[], int off, int len) {
    try {
      synchronized (this) {
        ensureOpen();
        out.write(buf, off, len);
        if (autoFlush) {
          out.flush();
        }
      }
    } catch (InterruptedIOException x) {
      Thread.currentThread().interrupt();
    } catch (IOException x) {
      trouble = true;
    }
  }

    /*
     * The following private methods on the text- and character-output streams
     * always flush the stream buffers, so that writes to the underlying byte
     * stream occur as promptly as with the original PrintStream.
     */

  private void write(char buf[]) {
    try {
      synchronized (this) {
        ensureOpen();
        textOut.write(buf);
        textOut.flushBuffer();
        charOut.flushBuffer();
        if (autoFlush) {
          for (int i = 0; i < buf.length; i++) {
            if (buf[i] == '\n') {
              out.flush();
            }
          }
        }
      }
    } catch (InterruptedIOException x) {
      Thread.currentThread().interrupt();
    } catch (IOException x) {
      trouble = true;
    }
  }

  private void write(String s) {
    try {
      synchronized (this) {
        ensureOpen();
        textOut.write(s);
        textOut.flushBuffer();
        charOut.flushBuffer();
        if (autoFlush && (s.indexOf('\n') >= 0)) {
          out.flush();
        }
      }
    } catch (InterruptedIOException x) {
      Thread.currentThread().interrupt();
    } catch (IOException x) {
      trouble = true;
    }
  }

  private void newLine() {
    try {
      synchronized (this) {
        ensureOpen();
        textOut.newLine();
        textOut.flushBuffer();
        charOut.flushBuffer();
        if (autoFlush) {
          out.flush();
        }
      }
    } catch (InterruptedIOException x) {
      Thread.currentThread().interrupt();
    } catch (IOException x) {
      trouble = true;
    }
  }

    /* Methods that do not terminate lines */

  /**
   * Prints a boolean value.  The string produced by <code>{@link
   * java.lang.String#valueOf(boolean)}</code> is translated into bytes
   * according to the platform's default character encoding, and these bytes
   * are written in exactly the manner of the
   * <code>{@link #write(int)}</code> method.
   *
   * @param b The <code>boolean</code> to be printed
   */
  public void print(boolean b) {
    write(b ? "true" : "false");
  }

  /**
   * Prints a character.  The character is translated into one or more bytes
   * according to the platform's default character encoding, and these bytes
   * are written in exactly the manner of the
   * <code>{@link #write(int)}</code> method.
   *
   * @param c The <code>char</code> to be printed
   */
  public void print(char c) {
    write(String.valueOf(c));
  }

  /**
   * Prints an integer.  The string produced by <code>{@link
   * java.lang.String#valueOf(int)}</code> is translated into bytes
   * according to the platform's default character encoding, and these bytes
   * are written in exactly the manner of the
   * <code>{@link #write(int)}</code> method.
   *
   * @param i The <code>int</code> to be printed
   * @see java.lang.Integer#toString(int)
   */
  public void print(int i) {
    write(String.valueOf(i));
  }

  /**
   * Prints a long integer.  The string produced by <code>{@link
   * java.lang.String#valueOf(long)}</code> is translated into bytes
   * according to the platform's default character encoding, and these bytes
   * are written in exactly the manner of the
   * <code>{@link #write(int)}</code> method.
   *
   * @param l The <code>long</code> to be printed
   * @see java.lang.Long#toString(long)
   */
  public void print(long l) {
    write(String.valueOf(l));
  }

  /**
   * Prints a floating-point number.  The string produced by <code>{@link
   * java.lang.String#valueOf(float)}</code> is translated into bytes
   * according to the platform's default character encoding, and these bytes
   * are written in exactly the manner of the
   * <code>{@link #write(int)}</code> method.
   *
   * @param f The <code>float</code> to be printed
   * @see java.lang.Float#toString(float)
   */
  public void print(float f) {
    write(String.valueOf(f));
  }

  /**
   * Prints a double-precision floating-point number.  The string produced by
   * <code>{@link java.lang.String#valueOf(double)}</code> is translated into
   * bytes according to the platform's default character encoding, and these
   * bytes are written in exactly the manner of the <code>{@link
   * #write(int)}</code> method.
   *
   * @param d The <code>double</code> to be printed
   * @see java.lang.Double#toString(double)
   */
  public void print(double d) {
    write(String.valueOf(d));
  }

  /**
   * Prints an array of characters.  The characters are converted into bytes
   * according to the platform's default character encoding, and these bytes
   * are written in exactly the manner of the
   * <code>{@link #write(int)}</code> method.
   *
   * @param s The array of chars to be printed
   * @throws NullPointerException If <code>s</code> is <code>null</code>
   */
  public void print(char s[]) {
    write(s);
  }

  /**
   * Prints a string.  If the argument is <code>null</code> then the string
   * <code>"null"</code> is printed.  Otherwise, the string's characters are
   * converted into bytes according to the platform's default character
   * encoding, and these bytes are written in exactly the manner of the
   * <code>{@link #write(int)}</code> method.
   *
   * @param s The <code>String</code> to be printed
   */
  public void print(String s) {
    if (s == null) {
      s = "null";
    }
    write(s);
  }

  /**
   * Prints an object.  The string produced by the <code>{@link
   * java.lang.String#valueOf(Object)}</code> method is translated into bytes
   * according to the platform's default character encoding, and these bytes
   * are written in exactly the manner of the
   * <code>{@link #write(int)}</code> method.
   *
   * @param obj The <code>Object</code> to be printed
   * @see java.lang.Object#toString()
   */
  public void print(Object obj) {
    write(String.valueOf(obj));
  }


    /* Methods that do terminate lines */

  /**
   * Terminates the current line by writing the line separator string.  The
   * line separator string is defined by the system property
   * <code>line.separator</code>, and is not necessarily a single newline
   * character (<code>'\n'</code>).
   */
  public void println() {
    newLine();
  }

  /**
   * Prints a boolean and then terminate the line.  This method behaves as
   * though it invokes <code>{@link #print(boolean)}</code> and then
   * <code>{@link #println()}</code>.
   *
   * @param x The <code>boolean</code> to be printed
   */
  public void println(boolean x) {
    synchronized (this) {
      print(x);
      newLine();
    }
  }

  /**
   * Prints a character and then terminate the line.  This method behaves as
   * though it invokes <code>{@link #print(char)}</code> and then
   * <code>{@link #println()}</code>.
   *
   * @param x The <code>char</code> to be printed.
   */
  public void println(char x) {
    synchronized (this) {
      print(x);
      newLine();
    }
  }

  /**
   * Prints an integer and then terminate the line.  This method behaves as
   * though it invokes <code>{@link #print(int)}</code> and then
   * <code>{@link #println()}</code>.
   *
   * @param x The <code>int</code> to be printed.
   */
  public void println(int x) {
    synchronized (this) {
      print(x);
      newLine();
    }
  }

  /**
   * Prints a long and then terminate the line.  This method behaves as
   * though it invokes <code>{@link #print(long)}</code> and then
   * <code>{@link #println()}</code>.
   *
   * @param x a The <code>long</code> to be printed.
   */
  public void println(long x) {
    synchronized (this) {
      print(x);
      newLine();
    }
  }

  /**
   * Prints a float and then terminate the line.  This method behaves as
   * though it invokes <code>{@link #print(float)}</code> and then
   * <code>{@link #println()}</code>.
   *
   * @param x The <code>float</code> to be printed.
   */
  public void println(float x) {
    synchronized (this) {
      print(x);
      newLine();
    }
  }

  /**
   * Prints a double and then terminate the line.  This method behaves as
   * though it invokes <code>{@link #print(double)}</code> and then
   * <code>{@link #println()}</code>.
   *
   * @param x The <code>double</code> to be printed.
   */
  public void println(double x) {
    synchronized (this) {
      print(x);
      newLine();
    }
  }

  /**
   * Prints an array of characters and then terminate the line.  This method
   * behaves as though it invokes <code>{@link #print(char[])}</code> and
   * then <code>{@link #println()}</code>.
   *
   * @param x an array of chars to print.
   */
  public void println(char x[]) {
    synchronized (this) {
      print(x);
      newLine();
    }
  }

  /**
   * Prints a String and then terminate the line.  This method behaves as
   * though it invokes <code>{@link #print(String)}</code> and then
   * <code>{@link #println()}</code>.
   *
   * @param x The <code>String</code> to be printed.
   */
  public void println(String x) {
    synchronized (this) {
      print(x);
      newLine();
    }
  }

  /**
   * Prints an Object and then terminate the line.  This method calls
   * at first String.valueOf(x) to get the printed object's string value,
   * then behaves as
   * though it invokes <code>{@link #print(String)}</code> and then
   * <code>{@link #println()}</code>.
   *
   * @param x The <code>Object</code> to be printed.
   */
  public void println(Object x) {
    String s = String.valueOf(x);
    synchronized (this) {
      print(s);
      newLine();
    }
  }


  /**
   * A convenience method to write a formatted string to this output stream
   * using the specified format string and arguments.
   *
   * <p> An invocation of this method of the form <tt>out.printf(format,
   * args)</tt> behaves in exactly the same way as the invocation
   *
   * <pre>
   *     out.format(format, args) </pre>
   *
   * @param format A format string as described in <a href="../util/Formatter.html#syntax">Format
   * string syntax</a>
   * @param args Arguments referenced by the format specifiers in the format string.  If there are
   * more arguments than format specifiers, the extra arguments are ignored.  The number of
   * arguments is variable and may be zero.  The maximum number of arguments is limited by the
   * maximum dimension of a Java array as defined by <cite>The Java&trade; Virtual Machine
   * Specification</cite>. The behaviour on a <tt>null</tt> argument depends on the <a
   * href="../util/Formatter.html#syntax">conversion</a>.
   * @return This output stream
   * @throws java.util.IllegalFormatException If a format string contains an illegal syntax, a
   * format specifier that is incompatible with the given arguments, insufficient arguments given
   * the format string, or other illegal conditions.  For specification of all possible formatting
   * errors, see the <a href="../util/Formatter.html#detail">Details</a> section of the formatter
   * class specification.
   * @throws NullPointerException If the <tt>format</tt> is <tt>null</tt>
   * @since 1.5
   */
  public PrintStream printf(String format, Object... args) {
    return format(format, args);
  }

  /**
   * A convenience method to write a formatted string to this output stream
   * using the specified format string and arguments.
   *
   * <p> An invocation of this method of the form <tt>out.printf(l, format,
   * args)</tt> behaves in exactly the same way as the invocation
   *
   * <pre>
   *     out.format(l, format, args) </pre>
   *
   * @param l The {@linkplain java.util.Locale locale} to apply during formatting.  If <tt>l</tt> is
   * <tt>null</tt> then no localization is applied.
   * @param format A format string as described in <a href="../util/Formatter.html#syntax">Format
   * string syntax</a>
   * @param args Arguments referenced by the format specifiers in the format string.  If there are
   * more arguments than format specifiers, the extra arguments are ignored.  The number of
   * arguments is variable and may be zero.  The maximum number of arguments is limited by the
   * maximum dimension of a Java array as defined by <cite>The Java&trade; Virtual Machine
   * Specification</cite>. The behaviour on a <tt>null</tt> argument depends on the <a
   * href="../util/Formatter.html#syntax">conversion</a>.
   * @return This output stream
   * @throws java.util.IllegalFormatException If a format string contains an illegal syntax, a
   * format specifier that is incompatible with the given arguments, insufficient arguments given
   * the format string, or other illegal conditions.  For specification of all possible formatting
   * errors, see the <a href="../util/Formatter.html#detail">Details</a> section of the formatter
   * class specification.
   * @throws NullPointerException If the <tt>format</tt> is <tt>null</tt>
   * @since 1.5
   */
  public PrintStream printf(Locale l, String format, Object... args) {
    return format(l, format, args);
  }

  /**
   * Writes a formatted string to this output stream using the specified
   * format string and arguments.
   *
   * <p> The locale always used is the one returned by {@link
   * java.util.Locale#getDefault() Locale.getDefault()}, regardless of any
   * previous invocations of other formatting methods on this object.
   *
   * @param format A format string as described in <a href="../util/Formatter.html#syntax">Format
   * string syntax</a>
   * @param args Arguments referenced by the format specifiers in the format string.  If there are
   * more arguments than format specifiers, the extra arguments are ignored.  The number of
   * arguments is variable and may be zero.  The maximum number of arguments is limited by the
   * maximum dimension of a Java array as defined by <cite>The Java&trade; Virtual Machine
   * Specification</cite>. The behaviour on a <tt>null</tt> argument depends on the <a
   * href="../util/Formatter.html#syntax">conversion</a>.
   * @return This output stream
   * @throws java.util.IllegalFormatException If a format string contains an illegal syntax, a
   * format specifier that is incompatible with the given arguments, insufficient arguments given
   * the format string, or other illegal conditions.  For specification of all possible formatting
   * errors, see the <a href="../util/Formatter.html#detail">Details</a> section of the formatter
   * class specification.
   * @throws NullPointerException If the <tt>format</tt> is <tt>null</tt>
   * @since 1.5
   */
  public PrintStream format(String format, Object... args) {
    try {
      synchronized (this) {
        ensureOpen();
        if ((formatter == null)
            || (formatter.locale() != Locale.getDefault())) {
          formatter = new Formatter((Appendable) this);
        }
        formatter.format(Locale.getDefault(), format, args);
      }
    } catch (InterruptedIOException x) {
      Thread.currentThread().interrupt();
    } catch (IOException x) {
      trouble = true;
    }
    return this;
  }

  /**
   * Writes a formatted string to this output stream using the specified
   * format string and arguments.
   *
   * @param l The {@linkplain java.util.Locale locale} to apply during formatting.  If <tt>l</tt> is
   * <tt>null</tt> then no localization is applied.
   * @param format A format string as described in <a href="../util/Formatter.html#syntax">Format
   * string syntax</a>
   * @param args Arguments referenced by the format specifiers in the format string.  If there are
   * more arguments than format specifiers, the extra arguments are ignored.  The number of
   * arguments is variable and may be zero.  The maximum number of arguments is limited by the
   * maximum dimension of a Java array as defined by <cite>The Java&trade; Virtual Machine
   * Specification</cite>. The behaviour on a <tt>null</tt> argument depends on the <a
   * href="../util/Formatter.html#syntax">conversion</a>.
   * @return This output stream
   * @throws java.util.IllegalFormatException If a format string contains an illegal syntax, a
   * format specifier that is incompatible with the given arguments, insufficient arguments given
   * the format string, or other illegal conditions.  For specification of all possible formatting
   * errors, see the <a href="../util/Formatter.html#detail">Details</a> section of the formatter
   * class specification.
   * @throws NullPointerException If the <tt>format</tt> is <tt>null</tt>
   * @since 1.5
   */
  public PrintStream format(Locale l, String format, Object... args) {
    try {
      synchronized (this) {
        ensureOpen();
        if ((formatter == null)
            || (formatter.locale() != l)) {
          formatter = new Formatter(this, l);
        }
        formatter.format(l, format, args);
      }
    } catch (InterruptedIOException x) {
      Thread.currentThread().interrupt();
    } catch (IOException x) {
      trouble = true;
    }
    return this;
  }

  /**
   * Appends the specified character sequence to this output stream.
   *
   * <p> An invocation of this method of the form <tt>out.append(csq)</tt>
   * behaves in exactly the same way as the invocation
   *
   * <pre>
   *     out.print(csq.toString()) </pre>
   *
   * <p> Depending on the specification of <tt>toString</tt> for the
   * character sequence <tt>csq</tt>, the entire sequence may not be
   * appended.  For instance, invoking then <tt>toString</tt> method of a
   * character buffer will return a subsequence whose content depends upon
   * the buffer's position and limit.
   *
   * @param csq The character sequence to append.  If <tt>csq</tt> is <tt>null</tt>, then the four
   * characters <tt>"null"</tt> are appended to this output stream.
   * @return This output stream
   * @since 1.5
   */
  public PrintStream append(CharSequence csq) {
    if (csq == null) {
      print("null");
    } else {
      print(csq.toString());
    }
    return this;
  }

  /**
   * Appends a subsequence of the specified character sequence to this output
   * stream.
   *
   * <p> An invocation of this method of the form <tt>out.append(csq, start,
   * end)</tt> when <tt>csq</tt> is not <tt>null</tt>, behaves in
   * exactly the same way as the invocation
   *
   * <pre>
   *     out.print(csq.subSequence(start, end).toString()) </pre>
   *
   * @param csq The character sequence from which a subsequence will be appended.  If <tt>csq</tt>
   * is <tt>null</tt>, then characters will be appended as if <tt>csq</tt> contained the four
   * characters <tt>"null"</tt>.
   * @param start The index of the first character in the subsequence
   * @param end The index of the character following the last character in the subsequence
   * @return This output stream
   * @throws IndexOutOfBoundsException If <tt>start</tt> or <tt>end</tt> are negative,
   * <tt>start</tt> is greater than <tt>end</tt>, or <tt>end</tt> is greater than
   * <tt>csq.length()</tt>
   * @since 1.5
   */
  public PrintStream append(CharSequence csq, int start, int end) {
    CharSequence cs = (csq == null ? "null" : csq);
    write(cs.subSequence(start, end).toString());
    return this;
  }

  /**
   * Appends the specified character to this output stream.
   *
   * <p> An invocation of this method of the form <tt>out.append(c)</tt>
   * behaves in exactly the same way as the invocation
   *
   * <pre>
   *     out.print(c) </pre>
   *
   * @param c The 16-bit character to append
   * @return This output stream
   * @since 1.5
   */
  public PrintStream append(char c) {
    print(c);
    return this;
  }

}
